package com.rules.admin.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.base.Function;
import com.rules.admin.service.*;
import com.rules.admin.utils.Constant;
import com.gillion.grule.client.utils.Closure;
import com.gillion.grule.client.utils.Streams;

import com.google.common.collect.*;
import com.rules.admin.dao.ProjectDao;
import com.rules.admin.entity.Project;

import com.rules.admin.entity.RulePackage;
import com.rules.admin.entity.ProjectPermissions;
import com.rules.admin.vo.PackageRoleVO;
import com.rules.admin.vo.ProjectRoleVO;
import com.rules.admin.vo.RuleUser;
import com.rules.admin.login.UserHolder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class ProjectServiceImpl extends ServiceImpl<ProjectDao, Project> implements ProjectService {
   @Autowired
   private RuleUserRoleService ruleUserRoleService;
   @Autowired
   private PermissionService permissionService;
   @Autowired
   private RulePackageService rulePackageService;
   @Autowired
   private UserHolder<RuleUser> userHolder;
   @Autowired
   private UserGroupService userGroupService;

   public void delete(List<Long> ids) {
     this.removeByIds(ids);
   }

   public Project update(Project project) {
      this.updateById(project);
      return project;
   }



   public IPage<Project> queryPage(Project project, IPage<Project> page) {
      LambdaQueryWrapper<Project> queryWrapper = Wrappers.lambdaQuery();
      if (StringUtils.isNotBlank(project.getProjectName())) {
            queryWrapper.like(Project::getProjectName, "%" + project.getProjectName() + "%");
      }

      return this.page(page, queryWrapper);
   }

   public List<ProjectPermissions> getCurrentUserProjectOfPermission() {
      Long userId = this.userHolder.getUser().getId();
      List ownerIds = Lists.newArrayList();
      ownerIds.add(userId);

      //TODO get group ids from userHolder
      //ownerIds.addAll(((RuleUser)this.userHolder.getUser()).getGroupIds());


      return this.permissionService.selectProjectOfPermission(ownerIds);
   }

   public List<Project> getAll() {
     return this.list();
   }

   public Map<Long, ProjectRoleVO> getProjectRoleVOs(Long userId) {
      if (userId != null && userId != -1L) {
         return this.ruleUserRoleService.getByUserId(userId).getRoleId() == Constant.PERMISSION_ADMIN ? this.getAllProjectRoleForAdmin() : this.permissionService.getCurrentUserProjectRole(userId);
      } else {
         return Maps.newConcurrentMap();
      }
   }

   private Map<Long, ProjectRoleVO> getAllProjectRoleForAdmin() {
      List<RulePackage> rulePackages = this.rulePackageService.list();
      final ImmutableListMultimap<Long, RulePackage> packageGroupByProjectId = Multimaps.index(rulePackages, new Function<RulePackage, Long>() {
         public Long apply(RulePackage input) {
            return input.getProjectid();
         }
      });
      Set<Long> projectIds = packageGroupByProjectId.keySet();
      final Set<Integer> adminRoles = Sets.newConcurrentHashSet();
      List<RulePackage> list = this.rulePackageService.list();
      List<String> projectCodes = new ArrayList();

      for(int i = 0; i < list.size(); ++i) {
         projectCodes.add(((RulePackage)list.get(i)).getGroupId());
      }


      List<Project> projects = Lists.newArrayList();
      if (projectCodes.size() > 0) {
         if (projectCodes.size() >= 1000) {
            projects = this.queryProjectsByPiece(projectCodes);
         } else {
            LambdaQueryWrapper<Project>  queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(Project::getProjectCode, projectCodes);
            projects = this.list(queryWrapper);
         }
      }

      Set<Long> set = new HashSet(projectIds);

      for(int i = 0; i < ((List)projects).size(); ++i) {
         set.add(((Project)((List)projects).get(i)).getId());
      }

      adminRoles.add(0);
      final Map<Long, ProjectRoleVO> prjectRoleVos = Maps.newConcurrentMap();
      Streams.of(set).forEach(new Closure<Long>() {
         public void execute(Long input) {
            ProjectRoleVO projectRoleVo = new ProjectRoleVO();
            projectRoleVo.setId(input);
            projectRoleVo.setRoles(adminRoles);
            final Map<Long, PackageRoleVO> packageRoleVos = Maps.newConcurrentMap();
            Streams.of(packageGroupByProjectId.get(input)).forEach(new Closure<RulePackage>() {
               public void execute(RulePackage rulePackage) {
                  PackageRoleVO packageRoleVo = new PackageRoleVO();
                  packageRoleVo.setId(rulePackage.getId());
                  packageRoleVo.setRoles(adminRoles);
                  packageRoleVos.put(rulePackage.getId(), packageRoleVo);
               }
            });
            projectRoleVo.setPackageRoles(packageRoleVos);
            prjectRoleVos.put(input, projectRoleVo);
         }
      });
      return prjectRoleVos;
   }

   private List<Project> queryProjectsByPiece(List<String> projectCodes) {
      int piece = projectCodes.size() / 1000;
      List<Project> projects = Lists.newArrayList();

      for(int i = 0; i <= piece; ++i) {
         int begin = i * 1000;
         int end = 1000 * (i + 1);
         if (i == piece) {
            end = projectCodes.size();
         }

         List<String> subProjectCodes = projectCodes.subList(begin, end);

         LambdaQueryWrapper<Project> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.in(Project::getProjectCode, subProjectCodes);
         projects.addAll(this.list(queryWrapper));
      }

      return projects;
   }

   public List<String> getProjectCode(RuleUser ruleUser) {
      List<Long> groupIds = userGroupService.getGroupIds(ruleUser.getUserId());
      List ownerIds = Lists.newArrayList();
      ownerIds.add(ruleUser.getUserId());
      ownerIds.addAll(groupIds);
      List<ProjectPermissions> list = this.permissionService.selectProjectOfPermission(ownerIds);
      List<String> codes = new ArrayList();

      for(int i = 0; i < list.size(); ++i) {
         codes.add(list.get(i).getProjectCode());
      }

      return codes;
   }

   @Override
   public Project getByProjectCode(String projectCode) {
      LambdaQueryWrapper<Project> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(Project::getProjectCode, projectCode);
      return this.getOne(queryWrapper);
   }

   public List<Project> getChildList(String id) {
      LambdaQueryWrapper<Project> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(Project::getParentId, id);
      return this.list(queryWrapper);

   }

   public List<Project> getParentList() {
      LambdaQueryWrapper<Project> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.isNull(Project::getParentId).or().eq(Project::getParentId, "");
      return this.list(queryWrapper);
   }
}
